#include "streammanger.h"
namespace Media
{
	StreamManger::StreamManger(const std::string& url, bool enable_local_ip, std::string local_ip):m_url(url)
	{

		m_enable_local_ip = enable_local_ip;
		m_local_ip = local_ip;
	}

	StreamManger::~StreamManger()
	{
		this->delHls();
		this->delFLV();
	}

	bool StreamManger::setHLSParam(const HLSParam&hls_param)
	{
		m_init_hls_setting = true;
		m_hls_param = hls_param;
		return true;
	}

	bool StreamManger::setFLVParam(const FLVParam& flv_param)
	{
		m_init_flv_setting = true;
		m_flv_param = flv_param;
		return true;
	}


	// 运行线程负责解复用
	void StreamManger::run()
	{
		//AVFormatContext* pFormatCtx;

		do
		{
			//封装格式上下文
			//pFormatCtx = avformat_alloc_context();
			AVFormatContext* pFormatCtx = avformat_alloc_context();

			AVDictionary* opts = NULL;
	
			// 找到了rtsp
			if (m_url.find("rtsp://") != -1)
			{
				//采用tcp传输
				av_dict_set(&opts, "rtsp_transport", "tcp", 0);
			}

			//如果没有设置stimeout，那么把ipc网线拔掉，av_read_frame会阻塞（时间单位是微妙） 
			av_dict_set(&opts, "stimeout", "5000000", 0);

		
			Log::printInfo("拉流:" + m_url);
			if (avformat_open_input(&pFormatCtx, m_url.data(), NULL, &opts) != 0)
			{
				Log::printError("打开流失败" + m_url);
				avformat_free_context(pFormatCtx);
				continue;
			}

			//3.获取音视频信息
			if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
			{
				Log::printError("can't find stream info " + m_url);
				avformat_free_context(pFormatCtx);
				continue;
			}


			av_dump_format(pFormatCtx, 0, m_url.data(), 0);
			
			// 记录源流开始时间  至关重要的一步后边修正pts要用
			m_stream_start_time = pFormatCtx->start_time;

			int in_video_index = -1;
			int in_audio_index = -1;
			AVStream* in_video_stream = nullptr, * in_audio_stream = nullptr;
			for (uint32_t i = 0; i < pFormatCtx->nb_streams; i++)
			{
				// 挨个遍历流
				auto stream = pFormatCtx->streams[i];
				switch ((int)stream->codecpar->codec_type)
				{
				case AVMEDIA_TYPE_VIDEO:
				{
					in_video_index = i;
					in_video_stream = stream;
				}; break;
				case AVMEDIA_TYPE_AUDIO:
				{
					in_audio_index = i;
					in_audio_stream = stream;
				}; break;
				}
			}

			// 实例以音视频都有的流做示例
			if (in_video_index == -1 || in_audio_index == -1)
			{
				std::cout << "未找到音频或视频流" << std::endl;
				avformat_close_input(&pFormatCtx);
				avformat_free_context(pFormatCtx);
				continue;
			}

			p_decoder_encoder = new DecoderEncoder(in_video_stream, in_audio_stream);
			if (!p_decoder_encoder->start())
			{
				Log::printDebug("编解码器初始化失败");
				return;
			}

			
			// 如果视频不需要转码
			if (!p_decoder_encoder->isTranslaterVideoCodec(&video_translater_codec))
			{
				// 如果不需要转码
				if (!this->createCodec(&video_translater_codec, in_video_stream))
				{
					Log::printError("在不转码的情况下生成,上下文失败");
					return;
				}
				m_video_translate = false;
			}
			else
			{
				m_video_translate = true;
			}

			// 如果音频不需要转码
			if (!p_decoder_encoder->isTranslaterAudioCodec(&audio_translater_codec))
			{
				// 如果不需要转码
				if (!this->createCodec(&audio_translater_codec, in_audio_stream))
				{
					Log::printError("在不转码的情况下生成,上下文失败");
					return;
				}
				m_audio_translate = false;
			}
			else
			{
				m_audio_translate = true;
			}

			this->initMuxingStream(video_translater_codec, audio_translater_codec);

			auto t_start = std::chrono::system_clock::now();
			//编码数据
			AVPacket *packet = av_packet_alloc();
			// 转换时间的基底时间
			AVRational time_base;
			time_base.num = 1;
			time_base.den = AV_TIME_BASE;


			m_ffmpeg_start_time = av_gettime_relative();

			while (true)
			{
				if (!m_running)
				{
					break;
				}
				
				bool live_realtime_recover = false;
				// 这里实现 -re参数功能若，送入的是m3u8则需要启用该功能
				 {
				
					for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
						auto stream = pFormatCtx->streams[i];
						switch ((int)stream->codecpar->codec_type)
						{
						case AVMEDIA_TYPE_VIDEO:
						{
							int64_t pts = av_rescale(m_last_video_pts, 1000000, AV_TIME_BASE);
							int64_t now = av_gettime_relative() - m_ffmpeg_start_time;
							//std::cout << m_last_video_pts <<"  " << now << std::endl;
							if (m_last_video_pts > now)
							{
								live_realtime_recover = true;
							}
							
						}; break;
						case AVMEDIA_TYPE_AUDIO:
						{
							int64_t pts = av_rescale(m_last_audio_pts, 1000000, AV_TIME_BASE);
							int64_t now = av_gettime_relative() - m_ffmpeg_start_time;
							//std::cout << m_last_audio_pts << "  " << now << std::endl;
							if (m_last_audio_pts > now)
							{
								live_realtime_recover = true;
							}
						}; break;
						}
						
					}
				}

				// -re参数
				if (live_realtime_recover)
				{
					continue;
				}
				

				if (av_read_frame(pFormatCtx, packet) < 0)
				{
					break;
				}

				

				int index = packet->stream_index;
				if (index == in_video_index)
				{
					// 用I帧来判断这个流是不是该切了
					/*if (packet->flags & AV_PKT_FLAG_KEY)
					{
						Log::printInfo("src find I");
					} */

					if (packet->dts != AV_NOPTS_VALUE)
						packet->dts += av_rescale_q(-m_stream_start_time, time_base, in_video_stream->time_base);
					if (packet->pts != AV_NOPTS_VALUE)
						packet->pts += av_rescale_q(-m_stream_start_time, time_base, in_video_stream->time_base);

					// 使用时间基转换成当前时间
					m_last_video_pts = av_rescale_q(packet->dts, in_video_stream->time_base, time_base);
					if (packet->flags & AV_PKT_FLAG_KEY)
					{
						PacketInfo::log_packet(pFormatCtx, packet, "I");
					}
				
					// 如果视频要编码
					if (m_video_translate)
					{
						av_packet_rescale_ts(packet,
							in_video_stream->time_base,
							video_translater_codec->time_base);

						std::queue< std::shared_ptr<AVPacket> > packet_out;
						if (!p_decoder_encoder->addPacket(AVMEDIA_TYPE_VIDEO,
							packet,
							packet_out))
						{
							Log::printError("视频编解码失败");
							break;
						}
						while (!packet_out.empty())
						{
							auto packet_node = packet_out.front();
							this->addPacketToMuxing(ENUM_MEDIA_TYPE::VIDEO, packet_node.get());
							packet_out.pop();
						}
					}
					else
					{
						this->addPacketToMuxing(ENUM_MEDIA_TYPE::VIDEO, packet);
					}
					
				}
				else if (index == in_audio_index)
				{
					
					if (packet->dts != AV_NOPTS_VALUE)
						packet->dts += av_rescale_q(-m_stream_start_time, time_base, in_audio_stream->time_base);
					if (packet->pts != AV_NOPTS_VALUE)
						packet->pts += av_rescale_q(-m_stream_start_time, time_base, in_audio_stream->time_base);

					m_last_audio_pts = av_rescale_q(packet->dts, in_audio_stream->time_base, time_base);
					// 如果音频要编码
					if (m_audio_translate)
					{
						av_packet_rescale_ts(packet,
							in_audio_stream->time_base,
							audio_translater_codec->time_base);

						std::queue< std::shared_ptr<AVPacket> > packet_out;
						if (!p_decoder_encoder->addPacket(AVMEDIA_TYPE_AUDIO,
							packet,
							packet_out))
						{
							Log::printError("音频编解码失败");
							break;
						}
						while (!packet_out.empty())
						{
							auto packet_node = packet_out.front();
							this->addPacketToMuxing(ENUM_MEDIA_TYPE::AUDIO, packet_node.get());
							packet_out.pop();
						}
					}
					else
					{
						this->addPacketToMuxing(ENUM_MEDIA_TYPE::AUDIO, packet);
					}
					
				}
				
				av_packet_unref(packet);
			}
			auto t_end = std::chrono::system_clock::now();
			std::chrono::duration<double, std::milli> fp_ms = t_end - t_start;
			Log::printDebug("解码用时" + std::to_string(fp_ms.count()));
			
			// 删除hls客户端
			this->delHls();
			this->delFLV();
			delete p_decoder_encoder;
			p_decoder_encoder = nullptr;

			av_packet_free(&packet);
			avformat_close_input(&pFormatCtx);
			avformat_free_context(pFormatCtx);

			if (!m_video_translate)
			{
				avcodec_free_context(&video_translater_codec);
				video_translater_codec = nullptr;
			}

			if (!m_audio_translate)
			{
				avcodec_free_context(&audio_translater_codec);
				audio_translater_codec = nullptr;
			}

		} while (m_running);
	}

	bool StreamManger::createCodec(AVCodecContext** codec_context,AVStream *stream)
	{
		if (nullptr == stream)
		{
			Log::printError("输入的码流为空");
			return false;
		}


		//根据索引拿到对应的流
		const AVCodec* pCodec = avcodec_find_decoder(stream->codecpar->codec_id);
		if (!pCodec)
		{
			Log::printError("解码器打开失败");
			return false;
		}

		//申请一个解码上下文
		(* codec_context) = avcodec_alloc_context3(pCodec);
		if ((*codec_context) == nullptr)
		{
			Log::printError("解码器申请内存失败");
			return false;
		}

		// 复制时间基
		(*codec_context)->time_base = stream->time_base;

		//用流解码信息初始化编码参数
		if (avcodec_parameters_to_context((*codec_context), stream->codecpar) < 0)
		{
			Log::printError("拷贝解码器失败");
			avcodec_free_context(&(*codec_context));
			codec_context = nullptr;
			return false;
		}

		return true;
	}

	void StreamManger::addPacketToMuxing(const ENUM_MEDIA_TYPE& type, AVPacket* packet)
	{
		if (nullptr != p_hls_muxing)
		{
			std::shared_ptr<AVPacket> tmp_packet(av_packet_alloc(), [](AVPacket* packet_) {
				av_packet_unref(packet_);
				av_packet_free(&packet_);
			});
			// 复制内容
			av_packet_ref(tmp_packet.get(), packet);
			p_hls_muxing->addPacket(type, tmp_packet.get());
		}
		

		if (nullptr != p_flv_muxing)
		{
			std::shared_ptr<AVPacket> tmp_packet(av_packet_alloc(), [](AVPacket* packet) {
				av_packet_free(&packet);
				});
			// 复制内容
			av_packet_ref(tmp_packet.get(), packet);
			if (!p_flv_muxing->getNeedRestart())
			{
				p_flv_muxing->addPacket(type, packet);
			}
			else
			{
				this->delFLV();
				this->initFLV(video_translater_codec, audio_translater_codec);
			}
			
		}
	}



	bool StreamManger::delHls()
	{
		if (nullptr != p_hls_muxing)
		{
			delete p_hls_muxing;
			p_hls_muxing = nullptr;
		}
		return true;
	}

	bool StreamManger::initMuxingStream(AVCodecContext* in_video_codec, AVCodecContext* in_audio_codec)
	{
		if (in_video_codec != nullptr && in_audio_codec != nullptr)
		{
			if (m_init_hls_setting)
			{
				this->initHls(in_video_codec, in_audio_codec);
			}
			
			if (m_init_flv_setting)
			{
				this->initFLV(in_video_codec, in_audio_codec);
			}
			
		}
		else
		{
			Log::printError("StreamManger::initMuxingStream 码流内音频或视频流为空初始化打包失败");
			return false;
		}
		return true;
		
	}

	bool StreamManger::initHls(AVCodecContext* in_video_codec, AVCodecContext* in_audio_codec)
	{
		p_hls_muxing = new HLS(m_hls_param, in_video_codec, in_audio_codec);
		p_hls_muxing->start();
		if (!p_hls_muxing->getInitStatus())
		{
			Log::printError("StreamManger::initHls hls串流失败");
			delete p_hls_muxing;
			p_hls_muxing = nullptr;
		}

		return true;
	}

	// 删除flv
	bool StreamManger::delFLV()
	{
		if (nullptr != p_flv_muxing)
		{
			delete p_flv_muxing;
			p_flv_muxing = nullptr;
		}
		return true;
	}

	// 初始化flv拉流
	bool StreamManger::initFLV(AVCodecContext* in_video_codec, AVCodecContext* in_audio_codec)
	{
		p_flv_muxing = new FLV(m_flv_param, in_video_codec, in_audio_codec);
		p_flv_muxing->start();
		if (!p_flv_muxing->getInitStatus())
		{
			Log::printError("StreamManger::initHls hls串流失败");
			delete p_flv_muxing;
			p_flv_muxing = nullptr;
		}
		return true;
	}


}